using System;
using System.Diagnostics;
using System.Text;

using Pegasus.Diagnostics;

namespace Pegasus
{
	/// <summary>
	/// Provide a reoccurring date time classes the implments a daily reoccrance pattern.
	/// </summary>
	/// <example>
	///		<p>
	///			The following code schedule 10 actions on a scheduled of every 4 days.
	///		</p>
	///		<code>
	///		public Function()
	///		{
	///			DateTimeReoccurringDaily occurance = new DateTimeReoccurringDaily();
	///			occurance.RunTime = new DateTime( "2:00 pm" );
	///			occurance.EveryNumberOfDays = 4;
	///			
	///			DateTime nextOccurance = DateTime.Now;
	///			for( int x = 0; x &lt; 10; x++ )
	///			{
	///				nextOccurance = occurance.GetNextOccurrance( nextOccurance );
	///				ScheduleTaskOnDate( nextOccurance );
	///			}
	///		}
	///		</code>
	/// </example>
	[ Serializable ]
	public class DateTimeReoccurringDaily : DateTimeReoccurring
	{
		// Local Instance Values
		private DailyMode m_mode = DailyMode.Mode1;
		private int m_numberOfDays = 1;

		/// <summary>
		/// Initializes a new instance of the <see cref="T:DateTimeReoccurringDaily"/> class.
		/// </summary>
		public DateTimeReoccurringDaily()
		{
		}

		/// <summary>
		/// Gets or sets the mode.
		/// </summary>
		/// <value>The mode.</value>
		public DailyMode Mode
		{
			get
			{
				return m_mode;
			}

			set
			{
				m_mode = value;
			}
		}

		/// <summary>
		/// Get/Set the number of days between occurances (1 to 365).
		/// </summary>
		public int EveryNumberOfDays
		{
			get
			{
				return m_numberOfDays;
			}

			set
			{
				ParamCode.Assert( value > 0 && value < 366, "EveryNumberOfDays" );
				m_numberOfDays = value;
			}
		}

		/// <summary>
		/// Get the date and time of the occurrance of the pattern from the
		/// given date time.
		/// </summary>
		/// <param name="fromDate">The date to calculate the pattern from.</param>
		/// <returns>A <see cref="DateTime"/> of the next occurance.</returns>
		public override DateTime GetNextOccurrence( DateTime fromDate )
		{
			int moveDays = 0;
			DateTime baseDate = StartDate;
			DateTime nextDate = fromDate.Date + RunTime.TimeOfDay;

			// If the fromDate is before the start date then use the start 
			// state as the staring point.
			if( fromDate < baseDate )
			{
				return GetNextOccurrence( baseDate );
			}

			if( m_mode == DailyMode.Mode2 )
			{
				switch( fromDate.DayOfWeek )
				{
					case DayOfWeek.Sunday:
						nextDate = fromDate.Date.AddDays( 1 ) + RunTime.TimeOfDay;
						moveDays = 1;
						break;

					case DayOfWeek.Monday:
					case DayOfWeek.Tuesday:
					case DayOfWeek.Wednesday:
					case DayOfWeek.Thursday:
						nextDate = fromDate.Date + RunTime.TimeOfDay;
						moveDays = 1;
						break;

					case DayOfWeek.Friday:
						nextDate = fromDate.Date + RunTime.TimeOfDay;
						moveDays = 3;
						break;

					case DayOfWeek.Saturday:
						nextDate = fromDate.Date.AddDays( 2 ) + RunTime.TimeOfDay;
						moveDays = 2;
						break;
				}
			}
			else if( m_mode == DailyMode.Mode3 )
			{
				switch( fromDate.DayOfWeek )
				{
					case DayOfWeek.Saturday:
						nextDate = fromDate.Date + RunTime.TimeOfDay;
						moveDays = 1;
						break;

					case DayOfWeek.Sunday:
						nextDate = fromDate.Date + RunTime.TimeOfDay;
						moveDays = 6;
						break;

					case DayOfWeek.Monday:
						nextDate = fromDate.Date.AddDays( 5 ) + RunTime.TimeOfDay;
						break;

					case DayOfWeek.Tuesday:
						nextDate = fromDate.Date.AddDays( 4 ) + RunTime.TimeOfDay;
						break;

					case DayOfWeek.Wednesday:
						nextDate = fromDate.Date.AddDays( 3 ) + RunTime.TimeOfDay;
						break;

					case DayOfWeek.Thursday:
						nextDate = fromDate.Date.AddDays( 2 ) + RunTime.TimeOfDay;
						break;

					case DayOfWeek.Friday:
						nextDate = fromDate.Date.AddDays( 1 ) + RunTime.TimeOfDay;
						break;
				}
			}
			else
			{
				TimeSpan dateDiff = fromDate - baseDate;
				int modDate = dateDiff.Days % m_numberOfDays;

				nextDate = fromDate.AddDays( modDate ).Date;
				nextDate += RunTime.TimeOfDay;

				// Set the move days if this is a run day.
				if( modDate == 0 )
				{
					moveDays = m_numberOfDays;
				}
			}

			// If the date is today but the run time has passed then move the day out. 
			if( nextDate.Date == fromDate.Date && nextDate < fromDate )
			{
				nextDate = nextDate.AddDays( moveDays );
			}

			return nextDate;
		}

		/// <summary>
		/// Get the text description for the occurrence.
		/// </summary>
		/// <returns></returns>
		public override string GetDisplayText()
		{
			StringBuilder msg = new StringBuilder();

			if( m_mode == DailyMode.Mode2 )
			{
				msg.Append( "Every weekday " );
			}
			else if( m_mode == DailyMode.Mode3 )
			{
				msg.Append( "Every weekend " );
			}
			else 
			{
				if( m_numberOfDays > 1 )
				{
					msg.Append( "Every " ).Append( m_numberOfDays ).Append( " days" );
				}
				else
				{
					msg.Append( "Everyday" );
				}
			}

			msg.Append( " at " ).Append( RunTime.ToShortTimeString() )
				.Append( ", stating on " ).Append( StartDate.ToShortDateString() );

			return msg.ToString();
		}

		/// <summary>
		/// 
		/// </summary>
		public enum DailyMode
		{
			/// <summary>Uses the EveryNumberOfDays property to set the reoccurence pattern.</summary>
			Mode1,

			/// <summary>Occures on every weekday. EveryNumberOfDays property is ignored.</summary>
			Mode2,

			/// <summary>Occures on every weekend day. EveryNumberOfDays property is ignored.</summary>
			Mode3,
		}
	}
}
